package com.sec.android.app.myfiles.widget.listview.pinchview;

import android.animation.ValueAnimator;
import android.view.View;
import android.view.ViewGroup;

import com.sec.android.app.myfiles.log.Log;

import java.util.ArrayList;

/**
 * @hide
 */
public class PinchAnimator extends ValueAnimator implements ValueAnimator.AnimatorUpdateListener {
    public static final int ANI_TYPE_NONE = 0;
    public static final int ANI_TYPE_TRANS_X = 1;
    public static final int ANI_TYPE_TRANS_Y = 2;
    public static final int ANI_TYPE_WIDTH = 7;
    public static final int ANI_TYPE_HEIGHT = 8;

    private static final int KEY_FRAME_COUNT = 2;

    private float mCurrentValue = 0.0f;
    private long mOriginStartDelay = 0;

    private ArrayList<View> mViews = new ArrayList<>();
    private ArrayList<PropertyHolder> mProperties = new ArrayList<>();

    public PinchAnimator() {
        setFloatValues(0.0f, 1.0f);
        addUpdateListener(this);
    }

    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        float curValue = (Float) animation.getAnimatedValue();
        mCurrentValue = curValue;

        for (int i = 0; i < mViews.size(); i++) {
            mProperties.get(i).updateProperty(mViews.get(i), curValue);
        }
    }

    public boolean addProperty(View view, int property, float start, float end) {
        if (view == null) {
            return false;
        }

        PropertyHolder holder = null;
        for (int i = 0; i < mViews.size(); i++) {
            if (view == mViews.get(i)) {
                holder = mProperties.get(i);
                break;
            }
        }

        if (holder == null) {
            holder = new PropertyHolder();
            if (!holder.addProperty(property, start, end)) {
                return false;
            }

            mViews.add(view);
            mProperties.add(holder);
        } else {
            if (!holder.addProperty(property, start, end)) {
                return false;
            }
        }

        return true;
    }

    @Override
    public void setStartDelay(long startDelay) {
        mOriginStartDelay = startDelay;
        super.setStartDelay(startDelay);
    }

    @Override
    public void setCurrentPlayTime(long playTime) {
        if (mOriginStartDelay > playTime) {
            super.setStartDelay(mOriginStartDelay - playTime);
            super.setCurrentPlayTime(0);
        } else {
            super.setStartDelay(0);
            super.setCurrentPlayTime(playTime - mOriginStartDelay);
        }
    }

    public void setStartPoint() {
        setFloatValues(mCurrentValue, 1.0f);
        if (mCurrentValue > 0.0f) {
            long aNewDuration = (long) (getDuration() * (1.0f - mCurrentValue));
            setDuration(aNewDuration);
            setCurrentPlayTime(0);
        }
    }

    public void reversePoint() {
        setFloatValues(mCurrentValue, 0.0f);

        if (mCurrentValue > 0.0f) {
            long aNewDuration = (long) (getDuration() * mCurrentValue);
            setDuration(aNewDuration);
            setCurrentPlayTime(0);
        }
    }

    private static class PropertyHolder {
        public static int START = 0;
        public static int END = 1;
        public static int DELTA = 2;

        private ArrayList<Integer> mTypes = new ArrayList<>();

        private float[] mX = new float[KEY_FRAME_COUNT + 1];
        private float[] mY = new float[KEY_FRAME_COUNT + 1];
        private float[] mWidth = new float[KEY_FRAME_COUNT + 1];
        private float[] mHeight = new float[KEY_FRAME_COUNT + 1];

        public PropertyHolder() {

        }

        public boolean addProperty(int type, float start, float end) {
            switch (type) {
                case ANI_TYPE_TRANS_X:
                    mX[START] = start;
                    mX[END] = end;
                    mX[DELTA] = mX[END] - mX[START];
                    addTypeToArray(type);
                    break;
                case ANI_TYPE_TRANS_Y:
                    mY[START] = start;
                    mY[END] = end;
                    mY[DELTA] = mY[END] - mY[START];
                    addTypeToArray(type);
                    break;
                case ANI_TYPE_WIDTH:
                    mWidth[START] = start;
                    mWidth[END] = end;
                    mWidth[DELTA] = mWidth[END] - mWidth[START];
                    addTypeToArray(type);
                    break;
                case ANI_TYPE_HEIGHT:
                    mHeight[START] = start;
                    mHeight[END] = end;
                    mHeight[DELTA] = mHeight[END] - mHeight[START];
                    addTypeToArray(type);
                    break;
                default:
                    return false;
            }

            return true;
        }

        private boolean addTypeToArray(int type) {
            return mTypes.add(type);
        }

        public boolean updateProperty(View view, float factor) {
            if (view == null) {
                Log.d(this, "updateProperty : Target view is null");
                return false;
            }

            float curValue;
            float posX = 0, posY = 0;
            int width = 0, height = 0;
            for (int property : mTypes) {
                switch (property) {
                    case ANI_TYPE_TRANS_X:
                        curValue = mX[START] + mX[DELTA] * factor;
                        posX = curValue;
                        break;
                    case ANI_TYPE_TRANS_Y:
                        curValue = mY[START] + mY[DELTA] * factor;
                        posY = curValue;
                        break;
                    case ANI_TYPE_WIDTH:
                        curValue = mWidth[START] + mWidth[DELTA] * factor;
                        width = (int) curValue;
                        break;
                    case ANI_TYPE_HEIGHT:
                        curValue = mHeight[START] + mHeight[DELTA] * factor;
                        height = (int) curValue;
                        break;
                    default:
                        break;
                }

                updateView(view, posX, posY, width, height);
            }
            return true;
        }
    }

    public static void updateView(View view, float x, float y, int width, int height) {
        ViewGroup.LayoutParams param;
        param = view.getLayoutParams();
        view.setX(x);
        view.setY(y);
        param.width = width;
        param.height = height;
        view.setLayoutParams(param);
    }
}
